home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Ham Radio 2000
/
Ham Radio 2000.iso
/
ham2000
/
qex
/
qexbert
/
comport.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-09-18
|
7KB
|
343 lines
/* Functions to provide interrupt-driven serial I/O for the IBM PC.
From May, 1989 issue of _Personal Engineering_ magazine.
*/
#include <stdio.h>
#include <alloc.h>
#include <bios.h>
#include <dos.h>
#include <string.h>
#include "comport.h"
#define timeout 10000 /* Read_ln timeour, millisec */
#define thr (com->base_addr)
#define rbr (com->base_addr)
#define ier (com->base_addr+1)
#define lcr (com->base_addr+3)
#define mcr (com->base_addr+4)
#define lsr (com->base_addr+5)
#define msr (com->base_addr+6)
static COMM *ports[4] = {NULL, NULL, NULL, NULL};
int comm_errno;
void com_isr(COMM *com);
static void interrupt
isr1(void)
{
com_isr(ports[0]);
}
static void interrupt
isr2(void)
{
com_isr(ports[1]);
}
static void interrupt
isr3(void)
{
com_isr(ports[2]);
}
static void interrupt
isr4(void)
{
com_isr(ports[3]);
}
static void interrupt (*isrs[])() = {isr1, isr2, isr3, isr4};
void
com_isr(COMM *com)
{
com->buffer[com->buffer_in++] = inportb(rbr); /* Get/store input byte */
if (com->buffer_in == com->max_buffer) /* Wrap input pointer */
com->buffer_in = 0;
com->buffer_length++;
if (com->buffer_length > com->max_buffer)
{ /* Buffer overflow */
com->buffer_length = com->max_buffer;
com->overrun_flag = 1;
}
if (com->shake_type && !com->bf_hndshk)
{
if (com->buffer_length > com->near_full)
{ /* Handshake off */
if (com->shake_type & CM_RTS)
outportb(mcr, 9);
if (com->shake_type & CM_XOFF)
{
while ((inportb(lsr) & 0x20) == 0)
;
outportb(thr, 0x13);
}
com->bf_hndshk = 1; /* flag true */
}
}
outportb(0x20, 0x20); /* EOI to 8279 */
}
/* Reset (empty) input buffer */
void
reset_buffer(COMM *com)
{
disable();
com->bf_hndshk = 0;
com->buffer_in = 0;
com->buffer_out = 0;
com->buffer_length = 0;
com->overrun_flag = 0;
enable();
}
COMM *
open_com(int cport, /* COM port, 1 or 2 */
unsigned int baud, /* Baud rate, 110-38400 */
int parity, /* 0 = no parity, 1 = odd, 2 = even */
int stopbits, /* Stop bits, 1 or 2 */
int numbits, /* # data bits, 7 or 8 */
int shaketype, /* 0 = no handshake, 1 = RTS handshake */
unsigned int buflen) /* Size of receive buffer */
{
int comdata;
int portidx;
int divisor;
int ptemp;
COMM *com;
if (numbits < 7 || numbits > 8 || stopbits < 1 || stopbits > 2 ||
parity < 0 || parity > 3 || baud < 50 || baud > 38400)
{
comm_errno = CM_INVALID;
return NULL;
}
for (portidx = 0; portidx < sizeof ports / sizeof ports[0]; portidx++)
if (ports[portidx] == NULL)
break;
if (portidx == sizeof ports / sizeof ports[0])
{
comm_errno = CM_TOOMANY;
return NULL;
}
comm_errno = 0;
if ((com = calloc(1, sizeof (COMM))) == NULL)
{
comm_errno = CM_NOMEMORY;
return NULL;
}
if ((com->buffer = malloc(buflen)) == NULL)
{
comm_errno = CM_NOMEMORY;
free(com);
return NULL;
}
com->port = cport;
com->portidx = portidx;
ports[portidx] = com;
com->near_full = ((long) buflen * 9L) / 10L;
com->near_empty = com->near_full / 9;
com->max_buffer = buflen;
com->shake_type = shaketype;
comdata = 0;
comdata |= numbits - 5;
comdata |= (stopbits -1) << 2;
comdata |= parity << 3;
divisor = 115200L / (long) baud;
switch (cport)
{
default:
case 1:
com->base_addr = 0x3f8;
com->intlev = 0xc;
break;
case 2:
com->base_addr = 0x2f8;
com->intlev = 0xb;
break;
case 3:
com->base_addr = 0x3e8;
com->intlev = 0xc;
break;
case 4:
com->base_addr = 0x2e8;
com->intlev = 0xb;
break;
}
com->oldfunc = getvect(com->intlev);
setvect(com->intlev, isrs[portidx]);
disable();
ptemp = inportb(lcr) & 0x7f;
outportb(lcr, ptemp | 0x80);
outportb(thr, divisor);
outport(ier, divisor >> 8);
outportb(lcr, comdata);
inportb(lsr); /* Reset any errors */
inportb(rbr);
switch (cport)
{
default:
case 1:
case 3:
outportb(0x21, inport(0x21) & 0xef);
break;
case 2:
case 4:
outportb(0x21, inportb(0x21) & 0xf7);
break;
}
outportb(ier, 1); /* Enable data-ready interrupt */
outportb(mcr, 0xb); /* RTS, DTR on, IRQ enabled */
reset_buffer(com);
printf("Using COM%d, IRQ %d, divisor=%d\n", com->port, com->intlev-8, divisor);
return com;
}
int
com_in(COMM *com)
{
int c;
comm_errno = 0;
if (com->buffer_length == 0)
return -1;
if (com->overrun_flag)
{
comm_errno = CM_OVERRUN;
com->overrun_flag = 0;
return -1;
}
c = com->buffer[com->buffer_out++] & 0xff;
if (com->buffer_out == com->max_buffer)
com->buffer_out = 0;
com->buffer_length--;
if (com->shake_type && com->bf_hndshk)
{
if (com->buffer_length < com->near_empty)
{
if (com->shake_type & CM_RTS)
outportb(mcr, 0xb);
if (com->shake_type & CM_XOFF)
{
while ((inportb(lsr) & 0x20) == 0)
;
outportb(thr, 0x11);
}
com->bf_hndshk = 0;
}
}
return c;
}
int
send_com(COMM *com,
char c, /* Character to send */
int handshake) /* Handshake type, 0=none, 1=CTS, 2=DSR, 3=CTS/DSR */
{
int counter;
int shakemask;
counter = 0;
comm_errno = 0;
switch (handshake)
{
default:
shakemask = 0;
break;
case 1:
shakemask = 0x10;
break;
case 2:
shakemask = 0x20;
break;
case 3:
shakemask = 0x30;
break;
}
do
{
if ((inportb(msr) & shakemask) == shakemask)
break;
if (counter >= timeout)
{
comm_errno = CM_TIMEOUT;
return -1;
}
counter++;
delay(1);
} while (1);
while ((inportb(lsr) & 0x20) == 0)
;
outportb(thr, c);
return 0;
}
int
comm_cd(COMM *com)
{
return (inportb(msr) & 0x80) ? 1 : 0;
}
int
comm_cts(COMM *com)
{
return (inportb(msr) & 0x10) ? 1 : 0;
}
int
comm_dsr(COMM *com)
{
return (inportb(msr) & 0x20) ? 1 : 0;
}
void
shut_down(COMM *com)
{
outportb(mcr, 0);
}
void
close_com(COMM *com)
{
if (com == NULL)
return;
disable();
outportb(0x21, inportb(0x21) | 0x18);
outportb(lcr, inportb(lcr) & 3);
outportb(ier, 0);
setvect(com->intlev, com->oldfunc);
enable();
ports[com->portidx] = NULL;
free(com->buffer);
free(com);
}
char *
comm_errmsg(int errno)
{
static char *errmsg[] = {
"No error",
"Insufficient memory",
"Too many comm ports open",
"Invalid argument",
"Receiver overrun",
"Transmitter timeout"
};
if (errno < 0 || errno > CM_NERRCODE)
return "Invalid COMM error number";
return errmsg[errno];
}
void
comm_perror(char *device)
{
fprintf(stderr, "%s: %s\n", device, comm_errmsg(comm_errno));
}